package cn.serein.charging.order.mqtt;

import cn.serein.charging.common.pojo.JsonResult;
import cn.serein.charging.common.pojo.param.ChargingProcessParam;
import cn.serein.charging.common.pojo.vo.ChargingProcessVO;
import cn.serein.charging.common.utils.JsonUtils;
import cn.serein.charging.order.constant.Constants;
import cn.serein.charging.order.constant.MqttConstant;
import cn.serein.charging.order.constant.OrderStatusConstant;
import cn.serein.charging.order.pojo.dto.ChargingProcessDto;
import cn.serein.charging.order.pojo.dto.ChargingResultDto;
import cn.serein.charging.order.pojo.po.ChargingBillFailPO;
import cn.serein.charging.order.pojo.po.ChargingBillSuccessPO;
import cn.serein.charging.order.pojo.po.ChargingProcessPO;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.*;
import org.springframework.beans.BeanUtils;

/**
 * EMQX的消息中间的 消费者
 * 订单服务 用来 接收 设备发送的消息
 */

@Slf4j
public class MqttConsumer implements MqttCallbackExtended {

    private MqttClient mqttClient;

    /**
     * TODO:  逻辑还不清晰
     * @param mqttClient
     */
    private MqttContext mqttContext;

    // 构造
    public MqttConsumer(MqttContext mqttContext,MqttClient mqttClient) {
        this.mqttContext = mqttContext;
        this.mqttClient = mqttClient;
    }

    @Override
    public void connectComplete(boolean reconnect, String serverURI) {
        log.debug("MqttConsumer#connectComplete方法被调用了,表示连接成功," +
                "reconnect:{},serverURI:{}",reconnect,serverURI);

        // 连接成功之后订阅
        try {
            log.debug("连接成功,订阅开始充电指令的响应topic:{}",MqttConstant.TOPIC_CHARGING_RESULT);
            mqttClient.subscribe(MqttConstant.TOPIC_CHARGING_RESULT);
            log.debug("连接成功,订阅开始充电指令的响应topic:{},成功.",MqttConstant.TOPIC_CHARGING_RESULT);


            // 同步充电进度的topic
            log.debug("连接成功,用来接收设备同步的topic:{}",MqttConstant.TOPIC_CHARGING_RESULT);
            mqttClient.subscribe(MqttConstant.TOPIC_CHARGING_PROCESS);
            log.debug("连接成功,用来接收设备同步的充电转台信息:{},成功.",MqttConstant.TOPIC_CHARGING_RESULT);
        } catch (MqttException e) {
            throw new RuntimeException(e);
        }

    }

    @Override
    public void connectionLost(Throwable cause) {
        log.debug("MqttConsumer#connectionLost方法被调用了,表示连接丢失",cause);
    }

    @Override
    public void messageArrived(String topic, MqttMessage message){
        log.debug("MqttConsumer#messageArrived方法被调用了,表示消息到达," +
                "topic:{},message:{}",topic,message);

        // 通过if else进行判断收到的topic
        if(MqttConstant.TOPIC_CHARGING_RESULT.equals(topic)){
            log.debug("处理开始充电的结果信息");
            // 封装成为方法
            handleChargingResult(message);
        }else if(MqttConstant.TOPIC_CHARGING_PROCESS.equals(topic)){
            log.debug("处理开始充电进度信息");
            handleChargingProcess(message);
        }



    }

    private void handleChargingProcess(MqttMessage message) {
        ChargingProcessDto chargingProcessDto = null;
        // 解析message
        try{
            chargingProcessDto =JsonUtils.fromJson(message.toString(), ChargingProcessDto.class);
        }catch (Exception e){
            log.error("设备发送的开始充电进度信息{}格式有问题,请检查!", message,e);
        }

        if(chargingProcessDto != null){


            // 获取收费
            ChargingProcessVO OrderCost = getOrderCost(chargingProcessDto);

            if(OrderCost != null){
                /**
                 * Todo:   计算费用之后需要考虑的事
                 */
                //保存充电进度数据
                saveChargingProcess(chargingProcessDto,OrderCost);
                //推送充电数据给用户
                sendChargingProcess2User(OrderCost);
                //判断余额 先充值,后充电
                checkUserBalance();
                //判断设备的状态(温度)
                checkDeviceStatus();
                //判断是否充满
                checkOrderStatus();
            }else{
                log.error("获取计费失败:{}",chargingProcessDto);
            }


        }
    }

    private ChargingProcessVO getOrderCost(ChargingProcessDto chargingProcessDto) {
        ChargingProcessParam chargingProcessParam = new ChargingProcessParam();
        BeanUtils.copyProperties(chargingProcessDto, chargingProcessParam);
        log.debug("调用计价服务入参:{}",chargingProcessParam);
        JsonResult<ChargingProcessVO> chargingProcessVOJsonResult =
                mqttContext.getCostClient().calculateCost(chargingProcessParam);
        log.debug("调用计价服务入参:{},出参:{}",chargingProcessParam,chargingProcessVOJsonResult);
        if (chargingProcessVOJsonResult != null) {
            return chargingProcessVOJsonResult.getData();
        }
        return null;
    }

    private void checkOrderStatus() {
    }

    private void checkDeviceStatus() {
        
    }

    private void checkUserBalance() {
        
    }

    private void sendChargingProcess2User(ChargingProcessVO chargingProcessVO) {
        String message = JsonUtils.toJson(chargingProcessVO);

        // mqttContext.getWebSocketServer().sendMessage(chargingProcessVO.getUserId(),message);
        Boolean result =  mqttContext.getWebSocketServer().sendMessage(chargingProcessVO.getUserId(),message);

        log.debug("向用户:{},发送充电进度信息:{},发送结果:{}",chargingProcessVO.getUserId(),message,result);
    }

    private void saveChargingProcess(ChargingProcessDto chargingProcessDto, ChargingProcessVO chargingProcessVO) {
        ChargingProcessPO chargingProcessPO = new ChargingProcessPO();

        chargingProcessPO.setId(System.currentTimeMillis());

        BeanUtils.copyProperties(chargingProcessDto,chargingProcessPO);

        chargingProcessPO.setTotalCost(chargingProcessVO.getTotalCost());

        mqttContext.getChargingProcessRepository().save(chargingProcessPO);
    }

    private void handleChargingResult(MqttMessage message) {
        //
        ChargingResultDto chargingResultDto = null;
        // 将Json转换为对象
        try{
            chargingResultDto = JsonUtils.fromJson(message.toString(), ChargingResultDto.class);

        }catch (Exception e) {
            log.error("设备发送的开始充电结果消息{}格式有问题,请检查!", message,e);
        }


        if(chargingResultDto != null){
            // 定义常量
            if (Constants.RESULT_START_CHARGING_SUCCESS.equals(chargingResultDto.getResult())) {
                /**
                 * 保存订单的业务封装成为方法
                 */
                saveSuccessOrder(chargingResultDto);


            }else {
                // 使用失败的mapper接口
                // 需要什么数据  订单id   充电桩的id   充电枪的id
                // 也可以将业务封装成为一个方法

                saveFailOrder(chargingResultDto);
            }
        }
    }

    /**
     * 保存失败订单
     * @param chargingResultDto
     */
    private void saveFailOrder(ChargingResultDto chargingResultDto) {
        log.debug("设备有响应,充电桩开始充电失败");
        log.debug("通知充电用户,表示歉意,提醒用户更换充电枪");
        log.debug("通知设备管理人员,进行设备检修");

        ChargingBillFailPO chargingBillFailPO = new ChargingBillFailPO();
        chargingBillFailPO.setBillId(chargingResultDto.getOrderNo());
        chargingBillFailPO.setFailDesc("充电站故障-无法正常使用");
        log.debug("保存失败订单记录:{}",chargingBillFailPO);
        Integer row = mqttContext.getOrderFailMapper().insert(chargingBillFailPO);
        // 打印日志查看是否保存成功
        log.debug("保存失败订单记录:{},影响行数:{}",chargingBillFailPO,row);
    }

    private void saveSuccessOrder(ChargingResultDto chargingResultDto) {
        /**
         * 下面的业务可以封装成为一个方法
         */
        log.debug("设备有响应,充电桩开始充电成功");
        log.debug("保存成功订单记录 ");
        ChargingBillSuccessPO po = new ChargingBillSuccessPO();
        po.setBillId(chargingResultDto.getOrderNo());
        po.setBillStatus(OrderStatusConstant.ORDER_STATUS_PROGRESS);
        log.debug("保存成功订单记录,{}", po);
        // 调用mapper,下面这中是mybatis写法
        // Integer row = mqttContext.getOrderSuccessMapper().saveOrderByMybatis(po);
        Integer row = mqttContext.getOrderSuccessMapper().insert(po);
        log.debug("保存成功订单记录:{},影响行数:{}", po,row);
    }

    @Override
    public void deliveryComplete(IMqttDeliveryToken token) {
        log.debug("MqttConsumer#deliveryComplete方法被调用了,表示消息分发完成" +
                "token:{}",token);
    }
}
